#include "stdafx.h"

#include <propkey.h>
#include <propvarutil.h>

 
#include  "qjumplist.h"
using namespace micro::app;




//#if (WINVER >= _WIN32_WINNT_WIN7)

QJumpList::QJumpList(LPCTSTR strid, bool bAutoCommit) 
{
	m_bInitialized = false;
	m_nMaxSlots = 0;
	m_bAutoCommit = bAutoCommit;	
	m_strAppID = strid;
	InitializeList();
}

QJumpList::~QJumpList()
{
	
	if (m_bInitialized && !m_bAutoCommit)
	{
		//QTRACE("Jump list is being destroyed without a call on QJumpList::CommitList.\n");
	}

	if (m_bAutoCommit)
	{
		// calls ClearAll as well
		// can be called when m_bInitialized is FALSE
		CommitList();
	}
	else
	{
		ClearAll();
	}
}

void QJumpList::ClearAll()
{
	 

	ClearAllDestinations();
	ReleaseObjectArray(m_tasksPtr.Detach());
	ReleaseObjectArray(m_removedItemsPtr.Detach());

	m_bInitialized = false;
}

BOOL QJumpList::InitializeList()
{
 
	if(m_bInitialized)
		return true;


	///
	if (m_appDests == nullptr) // already initialized
	{
		HRESULT hr = m_appDests.CoCreateInstance(CLSID_ApplicationDestinations, nullptr, CLSCTX_INPROC_SERVER);
		ASSERT (! m_strAppID.IsEmpty() );
		hr = m_appDests->SetAppID(m_strAppID);
	}


	HRESULT hr = S_OK;
	if (m_destListPtr == nullptr)
	{
		hr = m_destListPtr.CoCreateInstance(CLSID_DestinationList, nullptr, CLSCTX_INPROC_SERVER);
		if (FAILED(hr))
		{
			//QTRACE("Error initializing Jump List, code: ");
			return FALSE;
		}
	}

	if (m_tasksPtr == nullptr)
	{
		hr = m_tasksPtr.CoCreateInstance(CLSID_EnumerableObjectCollection, nullptr, CLSCTX_INPROC_SERVER);
		if (FAILED(hr))
		{
			//TRACE1("Error initializing task collection, code: %x", hr);
			return FALSE;
		}
	}

	 
	 
 
		m_destListPtr->SetAppID(m_strAppID);
 

		hr = m_destListPtr->BeginList(&m_nMaxSlots, IID_IObjectArray, (LPVOID*)&m_removedItemsPtr);
		if (FAILED(hr))
		{
			//TRACE1("ICustomDestinationList::BeginList failed, initialization canceled, HRESULT: %x", hr);
			return FALSE;
		}
		m_bInitialized = true;
 

	return TRUE;
}

UINT QJumpList::GetMaxSlots() const
{
	ASSERT(m_bInitialized);
	return m_nMaxSlots;
}

BOOL QJumpList::AddKnownCategory(KNOWNDESTCATEGORY category)
{
	  HRESULT  hr;

	/*UINT cMaxSlots;
	QComPointer<IObjectArray> pRemovedItems;
 
	hr = m_destListPtr->BeginList ( &cMaxSlots, IID_PPV_ARGS(&pRemovedItems) );
	 
	  if ( FAILED(hr) )
		return false;*/
	 
	  hr = m_destListPtr->AppendKnownCategory ( category );
	 
	  if ( FAILED(hr) )
		return false;
	 
	 // return SUCCEEDED( m_destListPtr->CommitList() );

 
	  return TRUE;
}

BOOL QJumpList::AddTask(LPCTSTR strTargetExecutablePath, LPCTSTR strCommandLineArgs, 
						LPCTSTR strTitle, LPCTSTR strIconPath, int iIconIndex)
{
	
	if (strTargetExecutablePath == nullptr || strTitle == nullptr)
	{
		return FALSE;
	}

	QComPointer<IShellLink> shellLinkPtr;
	if (FAILED(shellLinkPtr.CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER)))
	{
		return FALSE;
	}

	shellLinkPtr->SetPath(strTargetExecutablePath);
	shellLinkPtr->SetArguments(strCommandLineArgs);
	shellLinkPtr->SetIconLocation(strIconPath, iIconIndex);

	QComPointer<IPropertyStore> propPtr = shellLinkPtr;

	if (propPtr != nullptr)
	{
		PROPVARIANT var;

 
		if (FAILED(InitPropVariantFromString(strTitle, &var)))
		{
			return FALSE;
		}
 
		if (FAILED(propPtr->SetValue(PKEY_Title, var)))
		{
			PropVariantClear(&var);
			return FALSE;
		}

		HRESULT hr = propPtr->Commit();
		PropVariantClear(&var);
		if (FAILED(hr))
		{
			return FALSE;
		}
	}

	return  SUCCEEDED( m_tasksPtr->AddObject(shellLinkPtr.Detach() ) ) ;
}

BOOL QJumpList::AddTaskSeparator()
{
	

	QComPointer<IShellLink> shellLinkPtr;
	if (FAILED(shellLinkPtr.CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER)))
	{
		return FALSE;
	}

	QComPointer<IPropertyStore> propPtr = shellLinkPtr;
	PROPVARIANT var;
	var.vt = VT_BOOL;
	var.boolVal = VARIANT_TRUE;

	if (FAILED(propPtr->SetValue(PKEY_AppUserModel_IsDestListSeparator, var)))
	{
		PropVariantClear(&var);
		return FALSE;
	}

	HRESULT hr = propPtr->Commit();
	PropVariantClear(&var);
	if (FAILED(hr))
	{
		return FALSE;
	}

	return (SUCCEEDED(m_tasksPtr->AddObject(shellLinkPtr.Detach())));
}

BOOL QJumpList::AddTask(IShellLink* pShellLink)
{
	 

	ASSERT( pShellLink  );

	if (pShellLink == nullptr)
	{
		return FALSE;
	}

	 

	if (FAILED(m_tasksPtr->AddObject(pShellLink)))
	{
		return FALSE;
	}

	pShellLink->AddRef();
	return TRUE;
}

BOOL QJumpList::AddTasks(IObjectArray* pObjectCollection)
{
	

	ASSERT(pObjectCollection );

	if (pObjectCollection == nullptr)
	{
		return FALSE;
	}

	return SUCCEEDED(m_tasksPtr->AddFromArray(pObjectCollection));
}

BOOL QJumpList::AddDestination(LPCTSTR lpcszCategoryName, LPCTSTR lpcszDestinationPath)
{
	 
	ASSERT(lpcszCategoryName != nullptr);
	ASSERT(lpcszDestinationPath != nullptr);

	if (lpcszCategoryName == nullptr || lpcszDestinationPath == nullptr)
	{
		return FALSE;
	}

	IObjectCollection* pColl = GetObjectCollection(lpcszCategoryName);

	if (pColl == nullptr)
	{
		//TRACE0("AddDestination - GetObjectCollection returned nullptr collection.\n");
		return FALSE;
	}

	IShellItem* psi = nullptr;
 
	HRESULT hr =  SHCreateItemFromParsingName(lpcszDestinationPath, nullptr, IID_IShellItem, (LPVOID*)&psi);
 

	if (FAILED(hr))
	{
		//TRACE1("Can't obtain IShellItem from specified destination, HRESULT: %x.\n", hr);
		return FALSE;
	}

	hr = pColl->AddObject(psi);

	if (FAILED(hr))
	{
		//TRACE1("Add object to collection failed, HRESULT: %x.\n", hr);
		psi->Release();
		return FALSE;
	}

	return TRUE;
}

BOOL QJumpList::AddDestination(LPCTSTR lpcszCategoryName, IShellItem* pShellItem)
{
 
	ASSERT(pShellItem);

	if (pShellItem == nullptr)
	{
		return FALSE;
	}

	IObjectCollection* pColl = GetObjectCollection(lpcszCategoryName);

	if (pColl == nullptr)
	{
		return FALSE;
	}

	HRESULT hr = pColl->AddObject(pShellItem);

	if (FAILED(hr))
	{
		//TRACE1("Add object to collection failed, HRESULT: %x.\n", hr);
		return FALSE;
	}

	return TRUE;
}

BOOL QJumpList::AddDestination(LPCTSTR lpcszCategoryName, IShellLink* pShellLink)
{
	 

	ASSERT(pShellLink != nullptr);

	if (pShellLink == nullptr)
	{
		return FALSE;
	}

	IObjectCollection* pColl = GetObjectCollection(lpcszCategoryName);

	if (pColl == nullptr)
	{
		return FALSE;
	}

	HRESULT hr = pColl->AddObject(pShellLink);

	if (FAILED(hr))
	{
		//TRACE1("Add object to collection failed, HRESULT: %x.\n", hr);
		return FALSE;
	}

	return TRUE;
}

IObjectCollection* QJumpList::GetObjectCollection(LPCTSTR lpcszCategoryName)
{
 
	ASSERT(lpcszCategoryName);

	if (lpcszCategoryName == nullptr)
	{
		return nullptr;
	}

	IObjectCollection* pColl = nullptr;
	

	int len = m_mapDestinations.GetSize();
	for(int i=0; i<len; i++)
		if(m_mapDestinations[i].m_str == lpcszCategoryName)
		{
			pColl = m_mapDestinations[i].m_col;
			break;
		}



	if(pColl == nullptr)
	{
		HRESULT hr = CoCreateInstance(CLSID_EnumerableObjectCollection, nullptr, CLSCTX_INPROC_SERVER, 
			IID_IObjectCollection, (LPVOID*) &pColl);
		if (FAILED(hr))
		{
			//TRACE1("Creation of object collection failed with HRESULT: %x.\n", hr);
			return nullptr;
		}

		m_mapDestinations.Add( PPair(lpcszCategoryName,  pColl) );
	}

	return pColl;
}

BOOL QJumpList::CommitList()
{
	

	QString strCategory;
	IObjectCollection* pColl = nullptr;
	
	int len = m_mapDestinations.GetSize();
	for(int i=0; i<len; i++)
	{
		strCategory = m_mapDestinations[i].m_str;
		pColl = m_mapDestinations[i].m_col;
		 

		// remove items that could be in "removed" list from the collection being added  
		IObjectCollection* pFinalCollection = CheckRemovedItems(pColl);

		ASSERT(pFinalCollection != nullptr);
		if (pFinalCollection == nullptr)
		{
			//TRACE1("A final collection for Category \"%s\" is nullptr. This category is not committed. \n", strCategory);
			continue;
		}

		UINT nDocCount = 0;
		pFinalCollection->GetCount(&nDocCount);

		if (nDocCount == 0)
		{
			//TRACE1("WARNING: A final collection for Category \"%s\" is empty. \n", strCategory);
		}

 
		HRESULT hr = m_destListPtr->AppendCategory(strCategory, pFinalCollection);
 
		if(FAILED(hr))
		{
			//TRACE2("Append category %s failed , HRESULT: %x.\n", strCategory, hr);
		}

		ReleaseObjectArray(pFinalCollection);
	}

	ASSERT( m_tasksPtr );
	UINT nTaskCount = 0;
	m_tasksPtr->GetCount(&nTaskCount);

	if (nTaskCount > 0)
	{
		HRESULT hr = m_destListPtr->AddUserTasks(m_tasksPtr);
		if (FAILED(hr))
		{
			//TRACE1("Add tasks failed, code: %x\n", hr);
		}
		IObjectCollection* pTaskColl = m_tasksPtr.Detach();
		ReleaseObjectArray(pTaskColl);
	}

	m_mapDestinations.RemoveAll();

	BOOL bResult = TRUE;
	HRESULT hr = m_destListPtr->CommitList();

	if (FAILED(hr))
	{
		//TRACE1("CommitList failed, HRESULT: %x\n", hr);
		bResult = FALSE;
	}

	ClearAll(); //set m_bInitialized to FALSE

	// prevent calling CommitList for second time from destructor
	m_bAutoCommit = false;

	return bResult;
}

IObjectCollection* QJumpList::CheckRemovedItems(IObjectCollection* pColl)
{
	 

	ASSERT(pColl != nullptr);
	ASSERT(m_bInitialized);
	ASSERT(m_destListPtr != nullptr);

	if (m_removedItemsPtr == nullptr)
	{
		return pColl;
	}

	UINT nRemovedObjCount = 0; 
	UINT nCheckObjCount = 0;

	if (FAILED(m_removedItemsPtr->GetCount(&nRemovedObjCount)) || nRemovedObjCount == 0)
	{
		return pColl;
	}

	if (FAILED(pColl->GetCount(&nCheckObjCount)) || nCheckObjCount == 0)
	{
		return pColl;
	}

	IObjectCollection* pFinalCollection = nullptr;

	HRESULT hr = CoCreateInstance(CLSID_EnumerableObjectCollection, nullptr, CLSCTX_INPROC_SERVER, 
		IID_IObjectCollection, (LPVOID*) &pFinalCollection);

	if (FAILED(hr))
	{
		return pColl;
	}

	for (UINT i = 0; i < nCheckObjCount; i++)
	{
		IShellItem* pShellItemChk = nullptr;
		if (FAILED (pColl->GetAt(i, IID_IShellItem, (LPVOID*) &pShellItemChk)) || pShellItemChk == nullptr)
		{
			continue;
		}

		for (UINT j = 0; j < nRemovedObjCount; j++)
		{
			IShellItem* pShellItemRemoved = nullptr;
			if (FAILED (m_removedItemsPtr->GetAt(i, IID_IShellItem, (LPVOID*) &pShellItemRemoved)) || pShellItemRemoved == nullptr)
			{
				continue;
			}

			int nOrder = 0;
			if (SUCCEEDED(pShellItemChk->Compare(pShellItemRemoved, 0, &nOrder)) && nOrder != 0)
			{
				pFinalCollection->AddObject(pShellItemChk);
			}
			else
			{
				pShellItemChk->Release();
			}
		}
	}

	pColl->Release();
	return pFinalCollection;

}

void QJumpList::ClearAllDestinations()
{
 

	//QString strCategory;
	IObjectCollection* pColl = nullptr;
	
	int len = m_mapDestinations.GetSize();
	for(int i=0; i<len; i++)
	{
		//strCategory = m_mapDestinations[i].m_str;
		pColl = m_mapDestinations[i].m_col;

		ReleaseObjectArray(pColl);
	}

	m_mapDestinations.RemoveAll();
}

void QJumpList::ReleaseObjectArray(IObjectArray* pColl, BOOL bReleaseItemsOnly)
{
	if (pColl == nullptr)
	{
		return;
	}

	UINT nObjCount = 0;
	HRESULT hr = pColl->GetCount(&nObjCount);
	if (FAILED(hr))
	{
		return;
	}

	for (UINT i = 0; i < nObjCount; i++)
	{
		IUnknown* pUnk = nullptr;
		if (SUCCEEDED (pColl->GetAt(i, IID_IUnknown, (LPVOID*) &pUnk)) && pUnk != nullptr)
		{
			pUnk->Release();
		}
	}

	if (!bReleaseItemsOnly)
	{
		pColl->Release();
	}
}


  

///IApplicationDestinations

BOOL QJumpList::RemoveAppDest (IUnknown* pUnk)
{
	 
	ASSERT(m_appDests );
	ASSERT(pUnk);


	HRESULT hr = m_appDests->RemoveDestination(pUnk);

	if (FAILED(hr))
	{
		//TRACE1("RemoveDestination failed with code %x.", hr);
		return FALSE;
	}

	return TRUE;
}

BOOL QJumpList::RemoveAllAppDest ()
{
	 
	ASSERT(m_appDests );
	HRESULT hr = m_appDests->RemoveAllDestinations();

	if (FAILED(hr))
	{
		//TRACE1("RemoveAllDestinations failed with code %x.", hr);
		return FALSE;
	}

	return TRUE;
}
 



/* Clears the entire jumplist. */
void clear_jumplist(void)
{
	ICustomDestinationList *pCDL = nullptr;

	if (S_OK == CoCreateInstance(CLSID_DestinationList, nullptr, CLSCTX_INPROC_SERVER, IID_ICustomDestinationList, (void**) &pCDL ) ) 
	{
		pCDL->DeleteList( nullptr);
		pCDL->Release();
	}

}




//HRESULT AddJumpListTasks(std::list<JumpListTaskInformation> TaskList)
//{
//	if(TaskList.size() == 0)
//	{
//		return E_FAIL;
//	}
//
//	ICustomDestinationList *pCustomDestinationList = nullptr;
//	HRESULT hr;
//
//	hr = CoCreateInstance(CLSID_DestinationList,nullptr,CLSCTX_INPROC_SERVER,
//		IID_ICustomDestinationList,(LPVOID *)&pCustomDestinationList);
//
//	if(SUCCEEDED(hr))
//	{
//		IObjectArray *poa = nullptr;
//		UINT uMinSlots;
//
//		hr = pCustomDestinationList->BeginList(&uMinSlots,IID_IObjectArray,(void **)&poa);
//
//		if(SUCCEEDED(hr))
//		{
//			poa->Release();
//
//			IObjectCollection *poc = nullptr;
//
//			hr = CoCreateInstance(CLSID_EnumerableObjectCollection,nullptr,CLSCTX_INPROC_SERVER,
//				IID_IObjectCollection,(LPVOID *)&poc);
//
//			if(SUCCEEDED(hr))
//			{
//				AddJumpListTasksInternal(poc,TaskList);
//
//				hr = poc->QueryInterface(IID_IObjectArray,(void **)&poa);
//
//				if(SUCCEEDED(hr))
//				{
//					pCustomDestinationList->AddUserTasks(poa);
//					pCustomDestinationList->CommitList();
//
//					poa->Release();
//				}
//
//				poc->Release();
//			}
//		}
//
//		pCustomDestinationList->Release();
//	}
//
//	return hr;
//}
//
//HRESULT AddJumpListTasksInternal(IObjectCollection *poc,
//								 std::list<JumpListTaskInformation> TaskList)
//{
//	for each(auto jtli in TaskList)
//	{
//		AddJumpListTaskInternal(poc,jtli.pszName,
//			jtli.pszPath,jtli.pszArguments,
//			jtli.pszIconPath,jtli.iIcon);
//	}
//
//	return S_OK;
//}
//
//HRESULT AddJumpListTaskInternal(IObjectCollection *poc,TCHAR *pszName,
//								TCHAR *pszPath,TCHAR *pszArguments,TCHAR *pszIconPath,int iIcon)
//{
//	if(poc == nullptr ||
//		pszName == nullptr ||
//		pszPath == nullptr ||
//		pszArguments == nullptr ||
//		pszIconPath == nullptr)
//	{
//		return E_FAIL;
//	}
//
//	IShellLink *pShellLink = nullptr;
//	HRESULT hr;
//
//	hr = CoCreateInstance(CLSID_ShellLink,nullptr,CLSCTX_INPROC_SERVER,
//		IID_IShellLink,(LPVOID *)&pShellLink);
//
//	if(SUCCEEDED(hr))
//	{
//		pShellLink->SetPath(pszPath);
//		pShellLink->SetArguments(pszArguments);
//		pShellLink->SetIconLocation(pszIconPath,iIcon);
//
//		IPropertyStore *pps = nullptr;
//		PROPVARIANT pv;
//
//		hr = pShellLink->QueryInterface(IID_IPropertyStore,(void **)&pps);
//
//		if(SUCCEEDED(hr))
//		{
//			InitPropVariantFromString(pszName,&pv);
//
//			/* See: http://msdn.microsoft.com/en-us/library/bb787584(VS.85).aspx */
//			PROPERTYKEY PKEY_Title;
//			CLSIDFromString(L"{F29F85E0-4FF9-1068-AB91-08002B27B3D9}",&PKEY_Title.fmtid);
//			PKEY_Title.pid = 2;
//
//			pps->SetValue(PKEY_Title,pv);
//			pps->Commit();
//
//			poc->AddObject(pShellLink);
//
//			pps->Release();
//		}
//
//		pShellLink->Release();
//	}
//
//	return hr;
//}
















//
//bool MyFrame::ShowJumpListCategory ( KNOWNDESTCATEGORY category )
//{
//HRESULT hr;
//QComPtr<ICustomDestinationList> pDestList;
//
//    hr = pDestList.CoCreateInstance ( CLSID_DestinationList, nullptr, CLSCTX_INPROC_SERVER );
//
//    if ( FAILED(hr) )
//        return false;
//
//    hr = pDestList->SetAppID ( g_wszAppID );
//
//    if ( FAILED(hr) )
//        return false;
//
//	UINT cMaxSlots;
//	QComPtr<IObjectArray> pRemovedItems;
//
//    hr = pDestList->BeginList ( &cMaxSlots, IID_PPV_ARGS(&pRemovedItems) );
//
//    if ( FAILED(hr) )
//        return false;
//
//    hr = pDestList->AppendKnownCategory ( category );
//
//    if ( FAILED(hr) )
//        return false;
//
//    return SUCCEEDED( pDestList->CommitList() );
//}

//void MyFrame::OnClearJumpList ( UINT uCode, int nID, HWND hwndCtrl )
//{
//HRESULT hr;
//QComPointer<IApplicationDestinations> pDests;
//
//    hr = pDests.CoCreateInstance ( CLSID_ApplicationDestinations, nullptr,
//                                   CLSCTX_INPROC_SERVER );
//
//    if ( SUCCEEDED(hr) )
//        {
//        hr = pDests->SetAppID ( g_wszAppID );
//
//        if ( SUCCEEDED(hr) )
//            hr = pDests->RemoveAllDestinations();
//        }
//
//    if ( SUCCEEDED(hr) )
//        MessageBox ( _T("The jump list has been cleared") );
//    else
//        MessageBox ( _T("Error clearing the jump list") );
//}





/*

bool QJumpList::SetUpJumpList(LPCTSTR progID )
{
	
	QComPointer<ICustomDestinationList> pDestList;

    // Create a jump list COM object.
    HRESULT hr = pDestList.CoCreateInstance ( CLSID_DestinationList, nullptr, CLSCTX_INPROC_SERVER );

    if ( FAILED(hr) )
        return false;

    // Tell the jump list our AppID.
    hr = pDestList->SetAppID ( progID );

    if ( FAILED(hr) )
        return false;

    // Create a new, empty jump list. We aren't adding custom destinations,
    // so cMaxSlots and pRemovedItems aren't used.
	UINT cMaxSlots;
	QComPointer<IObjectArray> pRemovedItems;

    hr = pDestList->BeginList ( &cMaxSlots, IID_PPV_ARGS(&pRemovedItems) );

    if ( FAILED(hr) )
        return false;



    // Create an object collection to hold the custom tasks.
	/// IObjectCollection: public IObjectArray


	QComPointer<IObjectCollection> pObjColl;

    hr = pObjColl.CoCreateInstance ( CLSID_EnumerableObjectCollection, nullptr, CLSCTX_INPROC_SERVER );

    if ( FAILED(hr) )
        return false;

    // Add our custom tasks to the collection.
    if ( !AddJumpListTasks ( pObjColl ) )
        return false;

    // Get an IObjectArray interface for AddUserTasks.
	QComPointer<IObjectArray> pTasksArray = pObjColl;

    if ( !pTasksArray )
        return false;

    // Add the tasks to the jump list.
    hr = pDestList->AddUserTasks ( pTasksArray );
    
    if ( FAILED(hr) )
        return false;

    // Save the jump list.
    hr = pDestList->CommitList();

    return SUCCEEDED(hr);
}

bool QJumpList::AddJumpListTasks ( IObjectCollection* pObjColl )
{
QJumpListTask aTasks[] =
{
    { _T("/r"), _T("red text"),		_T("Red Text"),		IDI_ANY_FILE },
    { _T("/y"), _T("yellow text"),	_T("Yellow Text"),	IDI_ANY_FILE },
    { _T("/g"), _T("green text"),	_T("Green Text"),	IDI_ANY_FILE },
    { _T("/z"), _T("random colors"), _T("Random Colors"), IDI_ANY_FILE }
};

	// Get the path to the EXE, which we use as the path and icon path for each jump list task.
	TCHAR szExePath[MAX_PATH];
    GetModuleFileName ( nullptr, szExePath, _countof(szExePath) );

    for ( int i = 0; i < _countof(aTasks); i++ )
    {
		// Add a separator before the Random Colors task.
		if ( 3 == i && !AddJumpListSeparator ( pObjColl ) )
			;//QTRACE(_T("AddJump failed\n"));

		// Add the next task to the object collection.
		if ( !AddJumpListTask ( pObjColl, aTasks[i], szExePath ) )
			return false;
    }

    return true;
}

bool QJumpList::AddJumpListTask ( IObjectCollection* pObjColl, const QJumpListTask & rTask,
                       LPCTSTR szExePath )
{
    // Create a shell link COM object.
	HRESULT hr;
	QComPointer<IShellLink> pLink;

    hr = pLink.CoCreateInstance ( CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER );

    if ( FAILED(hr) )
        return false;

    // Set the executable path
    hr = pLink->SetPath ( szExePath );

    if ( FAILED(hr) )
        return false;

    // Set the arguments
    hr = pLink->SetArguments ( rTask.szArgs );

    if ( FAILED(hr) )
        return false;

    // Set the icon location
    hr = pLink->SetIconLocation ( szExePath, rTask.nIconIndex );

    if ( FAILED(hr) )
        return false;

    // Set the link description (tooltip on the jump list item)
    hr = pLink->SetDescription ( rTask.szDescription );

    if ( FAILED(hr) )
        return false;

    // Set the link title (the text of the jump list item). This is kept in the
    // object's property store, so QI for that interface.
	QComPointer<IPropertyStore> pPropStore = pLink;
	PROPVARIANT pv;

    if ( !pPropStore )
        return false;

    hr = InitPropVariantFromString (  rTask.szTitle , &pv );

    if ( FAILED(hr) )
        return false;

    // Set the title property.
    hr = pPropStore->SetValue ( PKEY_Title, pv );

    PropVariantClear ( &pv );

    if ( FAILED(hr) )
        return false;

    // Save the property changes.
    hr = pPropStore->Commit();

    if ( FAILED(hr) )
        return false;

    // Add this shell link to the object collection.
    hr = pObjColl->AddObject ( pLink );

    return SUCCEEDED(hr);
}

bool QJumpList::AddJumpListSeparator ( IObjectCollection* pObjColl )
{
    // Create a shell link COM object.
	HRESULT hr;
	QComPointer<IShellLink> pLink;

    hr = pLink.CoCreateInstance ( CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER );

    if ( FAILED(hr) )
        return false;

    // Get an IPropertyStore interface.
	QComPointer<IPropertyStore> pPropStore = pLink;
	PROPVARIANT pv;

    if ( !pPropStore )
        return false;

    hr = InitPropVariantFromBoolean ( TRUE, &pv );

    if ( FAILED(hr) )
        return false;

    // Set the property that makes the task a separator.
    hr = pPropStore->SetValue ( PKEY_AppUserModel_IsDestListSeparator, pv );

    PropVariantClear ( &pv );

    if ( FAILED(hr) )
        return false;

    // Save the property changes.
    hr = pPropStore->Commit();

    if ( FAILED(hr) )
        return false;

    // Add this shell link to the object collection.
    hr = pObjColl->AddObject ( pLink );

    return SUCCEEDED(hr);
}

*/